package com.bjf.commqry.util;

import java.io.File;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.bjf.common.ComFunc;
import com.bjf.common.Module;
import com.bjf.common.Rescode;
import com.bjf.commqry.bean.CommonQueryBean;
import com.bjf.commqry.bean.CommonQueryFieldBean;
import com.bjf.commqry.bean.CommonQueryOperationBean;
import com.bjf.commqry.bean.CommonQueryWhereBean;
import com.bjf.commqry.common.CommonQueryConstants;
import com.bjf.exception.BJFException;

public class CommonQueryParser {

	/** memeber variable: dom4j SAXReader. */
    private final SAXReader saxReader;

    /**
	 *
	 */
    public CommonQueryParser() {
        saxReader = new SAXReader();

    }
    
    public CommonQueryBean parse(Document document) throws BJFException {
    	CommonQueryBean bean = new CommonQueryBean();
    	parseTitle(bean, document);
    	parseHeightWidthRowCount(bean, document);
    	parseEditable(bean, document);
//    	parsePageSize(bean, document);
    	parseInit(bean, document);
    	parseType(bean, document);
    	parseDataAccessObjects(bean, document);
    	parseResultViewParam(bean, document);
//    	parseFieldsCount(bean, document);
    	parseFields(bean, document);
    	parseWhere(bean, document);
    	parseOperations(bean, document);
    	parseSql(bean, document);
    	parseGetter(bean, document);
    	parseScripts(bean, document);
    	return bean;
    }
    
    public CommonQueryBean parse(File configFile) throws BJFException {
    	try {
    	 Document document = saxReader.read(configFile);
         return parse(document);
    	} catch (Exception e) {
    		BJFException ex = new BJFException();
    		ex.initCause(e);
    		throw ex;
    	}
    }
    
    public CommonQueryBean parse(InputStream input) throws BJFException {
    	try {
    	 Document document = saxReader.read(input);
         return parse(document);
    	} catch (Exception e) {
    		BJFException ex = new BJFException();
    		ex.initCause(e);
    		throw ex;
    	}
    }
    
    /**
     * parse DataAccessObjects
     *
     * @param bean
     * @param documemt
     * @throws ComException
     */
    private void parseDataAccessObjects(CommonQueryBean bean, Document document)
            throws BJFException {
        try {
            List l = document.selectNodes("//"
                    + CommonQueryConstants.COMMON_FILE_ROOT + "/@"
                    + CommonQueryConstants.DATAACCESSOBJECTS);
            if (l.size() == 1) {
                Attribute daoEle = (Attribute) l.get(0);
                bean.setDataAccessObjects(daoEle.getValue());
            } else if (l.size() > 1) {
                throw new BJFException(
                        Module.SYSTEM_MODULE,
                        Rescode.COMMONQUERYPARSER_PARSE_COMMQRY_PARAM_DAO_ERROR,
                        "parse config file : "
                                + bean.toString()
                                + "The Size of DataAccessObjects Parameter GL One");
            } else {
                return;
            }
        } catch (BJFException appEx) {
            throw appEx;
        } catch (Exception ex) {
            throw new BJFException(Module.SYSTEM_MODULE,
                    Rescode.COMMONQUERYPARSER_PARSE_DAO_ERROR,
                    "parse config file : " + bean.toString()
                            + " DataAccessObjects Parameter error", ex);
        }
    }
    
    /**
     * parse FieldsCount
     *
     * @param bean
     * @param documemt
     * @throws ComException
     */
    private void parseFieldsCount(CommonQueryBean bean, Document document)
            throws BJFException {
        try {
        	 List frozenList = document.selectNodes("//"
                     + CommonQueryConstants.COMMON_FILE_ROOT + "/"
                     + CommonQueryConstants.FIELDS + "/@"
                     + CommonQueryConstants.FIELD_FROZEN_COUNT);
        	 List commonList = document.selectNodes("//"
                     + CommonQueryConstants.COMMON_FILE_ROOT + "/"
                     + CommonQueryConstants.FIELDS + "/@"
                     + CommonQueryConstants.FIELD_COMMON_COUNT);
            if (frozenList.size() == 1 && commonList.size() == 1) {
            	Attribute fEle = (Attribute) frozenList.get(0);
                Attribute cEle = (Attribute) commonList.get(0);
                bean.setFrozenCount(Integer.parseInt(fEle.getValue()));
                bean.setCommonCount(Integer.parseInt(cEle.getValue()));
            } else {
                return;
            }
        } catch (Exception ex) {
            throw new BJFException(Module.SYSTEM_MODULE,
                    Rescode.COMMONQUERYPARSER_PARSE_DAO_ERROR,
                    "parse config file : " + bean.toString()
                            + " DataAccessObjects Parameter error", ex);
        }
    }
    
    /**
     * parse Type
     *
     * @param bean
     * @param documemt
     * @throws ComException
     */
    private void parseType(CommonQueryBean bean, Document document)
            throws BJFException {
        try {
            List l = document.selectNodes("//"
                    + CommonQueryConstants.COMMON_FILE_ROOT + "/"
                    + CommonQueryConstants.COMMON_FILE_ROOT_TYPE);
            if (l.size() == 1) {
                Element daoEle = (Element) l.get(0);
                bean.setType(daoEle.getTextTrim());
            } else if (l.size() > 1) {
                throw new BJFException(
                        Module.SYSTEM_MODULE,
                        Rescode.COMMONQUERYPARSER_PARSE_COMMQRY_TYPE_ERROR,
                        "parse config file : "
                                + bean.toString()
                                + "The Type Parameter GL One");
            } else {
                return;
            }
        } catch (BJFException appEx) {
            throw appEx;
        } catch (Exception ex) {
            throw new BJFException(Module.SYSTEM_MODULE,
                    Rescode.COMMONQUERYPARSER_PARSE_COMMQRY_TITLE_ERROR,
                    "parse config file : " + bean.toString()
                            + " Title Parameter error", ex);
        }
    }
    
    /**
     * parse Title
     *
     * @param bean
     * @param documemt
     * @throws ComException
     */
    private void parseTitle(CommonQueryBean bean, Document document)
            throws BJFException {
        try {
            List l = document.selectNodes("//"
                    + CommonQueryConstants.COMMON_FILE_ROOT + "/@"
                    + CommonQueryConstants.COMMON_FILE_ROOT_TITLE);
            if (l.size() == 1) {
                Attribute daoEle = (Attribute) l.get(0);
                bean.setTitle(daoEle.getValue());
            } else if (l.size() > 1) {
                throw new BJFException(
                        Module.SYSTEM_MODULE,
                        Rescode.COMMONQUERYPARSER_PARSE_COMMQRY_TITLE_ERROR,
                        "parse config file : "
                                + bean.toString()
                                + "The Title Parameter GL One");
            } else {
                return;
            }
        } catch (BJFException appEx) {
            throw appEx;
        } catch (Exception ex) {
            throw new BJFException(Module.SYSTEM_MODULE,
                    Rescode.COMMONQUERYPARSER_PARSE_COMMQRY_TITLE_ERROR,
                    "parse config file : " + bean.toString()
                            + " Title Parameter error", ex);
        }
    }
    
    /**
     * parse Editable
     *
     * @param bean
     * @param documemt
     * @throws ComException
     */
    private void parseEditable(CommonQueryBean bean, Document document)
            throws BJFException {
        try {
            List l = document.selectNodes("//"
                    + CommonQueryConstants.COMMON_FILE_ROOT + "/@"
                    + CommonQueryConstants.COMMON_FILE_ROOT_EDITABLE);
            if (l.size() == 1) {
                Attribute daoEle = (Attribute) l.get(0);
                bean.setEditable(daoEle.getValue());
            } else if (l.size() > 1) {
                throw new BJFException(
                        Module.SYSTEM_MODULE,
                        Rescode.COMMONQUERYPARSER_PARSE_COMMQRY_EDITABLE_ERROR,
                        "parse config file : "
                                + bean.toString()
                                + "The Editable Parameter GL One");
            } else {
                return;
            }
        } catch (BJFException appEx) {
            throw appEx;
        } catch (Exception ex) {
            throw new BJFException(Module.SYSTEM_MODULE,
                    Rescode.COMMONQUERYPARSER_PARSE_COMMQRY_EDITABLE_ERROR,
                    "parse config file : " + bean.toString()
                            + " Editable Parameter error", ex);
        }
    }
    
    /**
     * parse pageSize
     *
     * @param bean
     * @param documemt
     * @throws ComException
     */
    private void parsePageSize(CommonQueryBean bean, Document document)
            throws BJFException {
        try {
            List l = document.selectNodes("//"
                    + CommonQueryConstants.COMMON_FILE_ROOT + "/@"
                    + CommonQueryConstants.COMMON_FILE_ROOT_PAGESIZE);
            if (l.size() == 1) {
                Attribute daoEle = (Attribute) l.get(0);
                bean.setPageSize(Integer.parseInt(daoEle.getValue()));
            } else if (l.size() > 1) {
                throw new BJFException(
                        Module.SYSTEM_MODULE,
                        Rescode.COMMONQUERYPARSER_PARSE_COMMQRY_PAGESIZE_ERROR,
                        "parse config file : "
                                + bean.toString()
                                + "The Pagesize Parameter GL One");
            } else {
                return;
            }
        } catch (BJFException appEx) {
            throw appEx;
        } catch (Exception ex) {
            throw new BJFException(Module.SYSTEM_MODULE,
                    Rescode.COMMONQUERYPARSER_PARSE_COMMQRY_PAGESIZE_ERROR,
                    "parse config file : " + bean.toString()
                            + " Pagesize Parameter error", ex);
        }
    }
    
    /**
     * parse Init
     *
     * @param bean
     * @param documemt
     * @throws ComException
     */
    private void parseInit(CommonQueryBean bean, Document document)
            throws BJFException {
        try {
            List l = document.selectNodes("//"
                    + CommonQueryConstants.COMMON_FILE_ROOT + "/@"
                    + CommonQueryConstants.COMMON_FILE_ROOT_INIT);
            if (l.size() == 1) {
                Attribute daoEle = (Attribute) l.get(0);
                bean.setInit(daoEle.getValue());
            } else if (l.size() > 1) {
                throw new BJFException(
                        Module.SYSTEM_MODULE,
                        Rescode.COMMONQUERYPARSER_PARSE_COMMQRY_INIT_ERROR,
                        "parse config file : "
                                + bean.toString()
                                + "The INIT Parameter GL One");
            } else {
                return;
            }
        } catch (BJFException appEx) {
            throw appEx;
        } catch (Exception ex) {
            throw new BJFException(Module.SYSTEM_MODULE,
                    Rescode.COMMONQUERYPARSER_PARSE_COMMQRY_INIT_ERROR,
                    "parse config file : " + bean.toString()
                            + " Init Parameter error", ex);
        }
    }
    
    /**
     * parse Height & Width
     *
     * @param bean
     * @param documemt
     * @throws ComException
     */
    private void parseHeightWidthRowCount(CommonQueryBean bean, Document document)
            throws BJFException {
        try {
            List hl = document.selectNodes("//"
                    + CommonQueryConstants.COMMON_FILE_ROOT + "/@"
                    + CommonQueryConstants.COMMON_FILE_ROOT_HEIGHT);
            List wl = document.selectNodes("//"
                    + CommonQueryConstants.COMMON_FILE_ROOT + "/@"
                    + CommonQueryConstants.COMMON_FILE_ROOT_WIDTH);
            List rl = document.selectNodes("//"
                    + CommonQueryConstants.COMMON_FILE_ROOT + "/@"
                    + CommonQueryConstants.COMMON_FILE_ROOT_ROWCOUNT);
            if (hl.size() == 1 && wl.size() == 1) {
                Attribute hAttr = (Attribute) hl.get(0);
                Attribute wAttr = (Attribute) wl.get(0);
                Attribute rAttr = (Attribute) rl.get(0);
                if(null != hAttr.getValue() && !hAttr.getValue().trim().equals(""))
                	bean.setHeight(Integer.parseInt(hAttr.getValue()));
                if(null != wAttr.getValue() && !wAttr.getValue().trim().equals(""))
                	bean.setWidth(Integer.parseInt(wAttr.getValue()));
                if(null != rAttr.getValue() && !rAttr.getValue().trim().equals(""))
                	bean.setRowCount(Integer.parseInt(rAttr.getValue()));
            } else {
                return;
            }
        } catch (Exception ex) {
            throw new BJFException(Module.SYSTEM_MODULE,
                    Rescode.COMMONQUERYPARSER_PARSE_COMMQRY_HEIGHTWIDTHROWCOUNT_ERROR,
                    "parse config file : " + bean.toString()
                            + " Height or Width or Rowcount Parameter error", ex);
        }
    }
    
    /**
     * parse Fields
     *
     * @param bean
     * @param documemt
     * @throws ComException
     */
    private void parseFields(CommonQueryBean bean, Document document)
            throws BJFException {
        try {
            List fieldList = document.selectNodes("//"
                    + CommonQueryConstants.COMMON_FILE_ROOT + "/"
                    + CommonQueryConstants.FIELDS + "/"
                    + CommonQueryConstants.FIELD);
            Element field;
            Iterator arIt;
            Attribute attribute;
            String arId, arVal;
            CommonQueryFieldBean cqField;
            for (int i = 0; i < fieldList.size(); i++) {
                field = (Element) fieldList.get(i);
                arIt = field.attributeIterator();
                cqField = new CommonQueryFieldBean();
                while (arIt.hasNext()) {
                    attribute = (Attribute) arIt.next();
                    arId = attribute.getName();
                    arVal = attribute.getValue();
                    cqField.setAnyValue(arId, arVal);
                }
                bean.putIntoField(cqField);
            }
        } catch (Exception ex) {
            throw new BJFException(Module.SYSTEM_MODULE,
                    Rescode.COMMONQUERYPARSER_PARSE_FIELDS_ERROR,
                    "parse config file : " + bean.toString()
                            + " Fields Parameter error", ex);
        }
    }

    /**
     * parse Where
     *
     * @param bean
     * @param documemt
     * @throws BJFException
     */
    private void parseWhere(CommonQueryBean bean, Document document)
            throws BJFException {
        try {
            List whereList = document.selectNodes("//"
                    + CommonQueryConstants.COMMON_FILE_ROOT + "/"
                    + CommonQueryConstants.WHERE);
            if (whereList.size() > 1) {
                throw new BJFException(Module.SYSTEM_MODULE,
                        Rescode.COMMONQUERYPARSER_PARSE_WHERE_ERROR,
                        "parse config file : " + bean.toString()
                                + " Where Parameter Error");
            }
            if (whereList.size() == 1) {
                Element whereEle = (Element) whereList.get(0);
                Iterator it = whereEle.elementIterator();
                Element whereParam;
                Attribute attr;
                String arId, arVal;
                while (it.hasNext()) {
                	CommonQueryWhereBean whereElement = new CommonQueryWhereBean();
                    whereParam = (Element) it.next();
                    Iterator wit = whereParam.attributeIterator();
                    while(wit.hasNext()) {
                    	attr = (Attribute)wit.next();
                    	arId = attr.getName();
                        arVal = attr.getValue();
                        if(arId.equals("operation"))
                        	arVal = changeOperation(arVal);
                        whereElement.setAnyValue(arId, arVal);
                    }
                    whereElement.setAnyValue("nameForType", whereParam.getName());
                    bean.putIntoWhere(whereElement);
                }
            }
        } catch (BJFException appEx) {
            throw appEx;
        } catch (Exception ex) {
            throw new BJFException(Module.SYSTEM_MODULE,
                    Rescode.COMMONQUERYPARSER_PARSERWHERE_ERROR,
                    "parse config file : " + bean.toString()
                            + " parse Where Fail", ex);
        }
    }
    
    /**
     * parse Operations
     *
     * @param bean
     * @param documemt
     * @throws BJFException
     */
    private void parseOperations(CommonQueryBean bean, Document document)
            throws BJFException {
        try {
            List operationsList = document.selectNodes("//"
                    + CommonQueryConstants.COMMON_FILE_ROOT + "/"
                    + CommonQueryConstants.OPERATIONS);
            if (operationsList.size() > 1) {
                throw new BJFException(Module.SYSTEM_MODULE,
                        Rescode.COMMONQUERYPARSER_PARSE_OPERATIONS_ERROR,
                        "parse config file : " + bean.toString()
                                + " Operations Parameter Error");
            }
            if (operationsList.size() == 1) {
                Element whereEle = (Element) operationsList.get(0);
                Iterator it = whereEle.elementIterator();
                Element operationParam;
                Attribute attr;
                String arId, arVal;
                while (it.hasNext()) {
                	CommonQueryOperationBean operationElement = new CommonQueryOperationBean();
                    operationParam = (Element) it.next();
                    Iterator wit = operationParam.attributeIterator();
                    while(wit.hasNext()) {
                    	attr = (Attribute)wit.next();
                    	arId = attr.getName();
                        arVal = attr.getValue();
                        operationElement.setAnyValue(arId, arVal);
                    }
                    operationElement.setAnyValue("nameForType", operationParam.getName());
                    bean.putIntoOperations(operationElement);
                }
            }
        } catch (BJFException appEx) {
            throw appEx;
        } catch (Exception ex) {
            throw new BJFException(Module.SYSTEM_MODULE,
                    Rescode.COMMONQUERYPARSER_PARSEROPERATIONS_ERROR,
                    "parse config file : " + bean.toString()
                            + " parse Operations Fail", ex);
        }
    }
    
    /**
     * parse Sql
     *
     * @param bean
     * @param documemt
     * @throws ComException
     */
    private void parseSql(CommonQueryBean bean, Document document)
            throws BJFException {
        try {
            List l = document.selectNodes("//"
                    + CommonQueryConstants.COMMON_FILE_ROOT + "/"
                    + CommonQueryConstants.SQL);
            if (l.size() == 1) {
                Element sqlEle = (Element) l.get(0);
                String sql = sqlEle.getTextTrim();
                String sqlChk = sql.toLowerCase();
                if (sqlChk.indexOf(CommonQueryConstants.SQL_SELECT) == -1
                        || sqlChk.indexOf(CommonQueryConstants.SQL_FROM) == -1) {
                    throw new BJFException(
                            Module.SYSTEM_MODULE,
                            Rescode.COMMONQUERYPARSER_PARSE_COMMQRY_PARAM_SQL_STRUCT_ERROR,
                            "parse config file : "
                                    + bean.toString()
                                    + " sql statement must contain 'select' and 'from'");
                }
                bean.setSql(sqlChk);
                bean.setCountSql(ComFunc.sqlToCountSql(sqlChk));
            } else if (l.size() > 1) {
                throw new BJFException(
                        Module.SYSTEM_MODULE,
                        Rescode.COMMONQUERYPARSER_PARSE_COMMQRY_PARAM_SQL_STRUCT_ERROR,
                        "parse config file : "
                                + bean.toString()
                                + "The sql statement GL One");
            } else {
                return;
            }
        } catch (BJFException appEx) {
            throw appEx;
        } catch (Exception ex) {
            throw new BJFException(Module.SYSTEM_MODULE,
                    Rescode.COMMONQUERYPARSER_PARSE_COMMQRY_PARAM_SQL_STRUCT_ERROR,
                    "parse config file : " + bean.toString()
                            + " sql statement error", ex);
        }
    }
    
    /**
     * parse Getter
     *
     * @param bean
     * @param documemt
     * @throws ComException
     */
    private void parseGetter(CommonQueryBean bean, Document document)
            throws BJFException {
        try {
            List l = document.selectNodes("//"
                    + CommonQueryConstants.COMMON_FILE_ROOT + "/"
                    + CommonQueryConstants.GETTER);
            if (l.size() == 1) {
                Element getterEle = (Element) l.get(0);
                String getter = getterEle.getTextTrim();
                bean.setGetter(getter);
            } else if (l.size() > 1) {
                throw new BJFException(
                        Module.SYSTEM_MODULE,
                        Rescode.COMMONQUERYPARSER_PARSE_COMMQRY_PARAM_SQL_STRUCT_ERROR,
                        "parse config file : "
                                + bean.toString()
                                + "The sql statement GL One");
            } else {
                return;
            }
        } catch (BJFException appEx) {
            throw appEx;
        } catch (Exception ex) {
            throw new BJFException(Module.SYSTEM_MODULE,
                    Rescode.COMMONQUERYPARSER_PARSE_COMMQRY_PARAM_SQL_STRUCT_ERROR,
                    "parse config file : " + bean.toString()
                            + " sql statement error", ex);
        }
    }
    
    /**
     * parse Scripts
     *
     * @param bean
     * @param documemt
     * @throws ComException
     */
    private void parseScripts(CommonQueryBean bean, Document document)
            throws BJFException {
        try {
            List l = document.selectNodes("//"
                    + CommonQueryConstants.COMMON_FILE_ROOT + "/"
                    + CommonQueryConstants.SCRIPTS);
            if (l.size() == 1) {
                Element getterEle = (Element) l.get(0);
                String scripts = getterEle.getTextTrim();
                bean.setScripts(scripts);
            } else if (l.size() > 1) {
                throw new BJFException(
                        Module.SYSTEM_MODULE,
                        Rescode.COMMONQUERYPARSER_PARSE_COMMQRY_PARAM_SQL_STRUCT_ERROR,
                        "parse config file : "
                                + bean.toString()
                                + "The scripts statement GL One");
            } else {
                return;
            }
        } catch (BJFException appEx) {
            throw appEx;
        } catch (Exception ex) {
            throw new BJFException(Module.SYSTEM_MODULE,
                    Rescode.COMMONQUERYPARSER_PARSE_COMMQRY_PARAM_SQL_STRUCT_ERROR,
                    "parse config file : " + bean.toString()
                            + " scripts statement error", ex);
        }
    }
    
    /**
     * parse ResultViewParam
     *
     * @param bean
     * @param documemt
     * @throws ComException
     */
    private void parseResultViewParam(CommonQueryBean bean, Document document)
            throws BJFException {
        try {
            List l = document.selectNodes("//"
                    + CommonQueryConstants.COMMON_FILE_ROOT + "/@"
                    + CommonQueryConstants.RESULTVIEWPARAM);
            if (l.size() == 1) {
                Attribute daoEle = (Attribute) l.get(0);
                bean.setResultViewParam(daoEle.getValue());
            } else if (l.size() > 1) {
                throw new BJFException(
                        Module.SYSTEM_MODULE,
                        Rescode.COMMONQUERYPARSER_PARSE_COMMQRY_PARAM_DAO_ERROR,
                        "parse config file : "
                                + bean.toString()
                                + "The Size of ResultViewParam Parameter GL One");
            } else {
                return;
            }
        } catch (BJFException appEx) {
            throw appEx;
        } catch (Exception ex) {
            throw new BJFException(Module.SYSTEM_MODULE,
                    Rescode.COMMONQUERYPARSER_PARSE_DAO_ERROR,
                    "parse config file : " + bean.toString()
                            + " ResultViewParam Parameter error", ex);
        }
    }
    
    private String changeOperation(String value) throws BJFException {
    	if(value.equals("lt")) {
    		value = "<";
    	} else if(value.equals("gt")) {
    		value = ">";
    	} else if(value.equals("lte")) {
    		value = "<=";
    	} else if(value.equals("gte")) {
    		value = ">=";
    	} else if(value.equals("ne")) {
    		value = "<>";
    	}
    	return value;
    }
}
